//#import "WebSocket.h"
//#import "HTTPMessage.h"
//#import "GCDAsyncSocket.h"
//#import "DDNumber.h"
//#import "DDData.h"
//#import "HTTPLogging.h"
//

import Foundation
//#if ! __has_feature(objc_arc)
//#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
//#endif
//
//// Log levels: off, error, warn, info, verbose
//// Other flags : trace
//static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
//
//#define TIMEOUT_NONE          -1
//#define TIMEOUT_REQUEST_BODY  10
//
//#define TAG_HTTP_REQUEST_BODY      100
//#define TAG_HTTP_RESPONSE_HEADERS  200
//#define TAG_HTTP_RESPONSE_BODY     201
//
//#define TAG_PREFIX                 300
//#define TAG_MSG_PLUS_SUFFIX        301
//#define TAG_MSG_WITH_LENGTH        302
//#define TAG_MSG_MASKING_KEY        303
//#define TAG_PAYLOAD_PREFIX         304
//#define TAG_PAYLOAD_LENGTH         305
//#define TAG_PAYLOAD_LENGTH16       306
//#define TAG_PAYLOAD_LENGTH64       307
//
//#define WS_OP_CONTINUATION_FRAME   0
//#define WS_OP_TEXT_FRAME           1
//#define WS_OP_BINARY_FRAME         2
//#define WS_OP_CONNECTION_CLOSE     8
//#define WS_OP_PING                 9
//#define WS_OP_PONG                 10
//
//static inline BOOL WS_OP_IS_FINAL_FRAGMENT(UInt8 frame)
//{
//	return (frame & 0x80) ? YES : NO;
//}
//
//static inline BOOL WS_PAYLOAD_IS_MASKED(UInt8 frame)
//{
//	return (frame & 0x80) ? YES : NO;
//}
//
//static inline NSUInteger WS_PAYLOAD_LENGTH(UInt8 frame)
//{
//	return frame & 0x7F;
//}
//
//@interface WebSocket (PrivateAPI)
//
//- (void)readRequestBody;
//- (void)sendResponseBody;
//- (void)sendResponseHeaders;
//
//@end
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark -
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
open class WebSocket : NSObject{
    //@implementation WebSocket
    //{
    //	BOOL isRFC6455;
    //	BOOL nextFrameMasked;
    //	NSUInteger nextOpCode;
    //	NSData *maskingKey;
    //}
    //
    //+ (BOOL)isWebSocketRequest:(HTTPMessage *)request
    open class func isWebSocketRequest(_ request:HTTPMessage)->Bool{
    //{
    //	// Request (Draft 75):
    //	// 
    //	// GET /demo HTTP/1.1
    //	// Upgrade: WebSocket
    //	// Connection: Upgrade
    //	// Host: example.com
    //	// Origin: http://example.com
    //	// WebSocket-Protocol: sample
    //	// 
    //	// 
    //	// Request (Draft 76):
    //	//
    //	// GET /demo HTTP/1.1
    //	// Upgrade: WebSocket
    //	// Connection: Upgrade
    //	// Host: example.com
    //	// Origin: http://example.com
    //	// Sec-WebSocket-Protocol: sample
    //	// Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
    //	// Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
    //	// 
    //	// ^n:ds[4U
    //	
    //	// Look for Upgrade: and Connection: headers.
    //	// If we find them, and they have the proper value,
    //	// we can safely assume this is a websocket request.
    //	
    	let upgradeHeaderValue = request.headerField("Upgrade");
    	let connectionHeaderValue = request.headerField("Connection");
        
    	
    	var isWebSocket = true;
    
    	if upgradeHeaderValue == nil
            || connectionHeaderValue == nil {
    		isWebSocket = false;
    	}
    	else if upgradeHeaderValue!.caseInsensitiveCompare("WebSocket") !=  ComparisonResult.orderedSame {
    		isWebSocket = false;
    	}
    	else if connectionHeaderValue!.range(of: "Upgrade", options:NSString.CompareOptions.caseInsensitive) == nil {
    		isWebSocket = false;
    	}
    	
//    	HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isWebSocket ? @"YES" : @"NO"));
    	
    	return isWebSocket;
    }
    //
    //+ (BOOL)isVersion76Request:(HTTPMessage *)request
    //{
    //	NSString *key1 = [request headerField:@"Sec-WebSocket-Key1"];
    //	NSString *key2 = [request headerField:@"Sec-WebSocket-Key2"];
    //	
    //	BOOL isVersion76;
    //	
    //	if (!key1 || !key2) {
    //		isVersion76 = NO;
    //	}
    //	else {
    //		isVersion76 = YES;
    //	}
    //	
    //	HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isVersion76 ? @"YES" : @"NO"));
    //	
    //	return isVersion76;
    //}
    //
    //+ (BOOL)isRFC6455Request:(HTTPMessage *)request
    //{
    //	NSString *key = [request headerField:@"Sec-WebSocket-Key"];
    //	BOOL isRFC6455 = (key != nil);
    //
    //	HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isRFC6455 ? @"YES" : @"NO"));
    //
    //	return isRFC6455;
    //}
    
    fileprivate var _websocketQueue:DispatchQueue;
    fileprivate var _request:HTTPMessage;
    fileprivate var _asyncSocket:GCDAsyncSocket;
    fileprivate var _isOpen = false;
    fileprivate var _isVersion76 = false;// = [[self class] isVersion76Request:request];
    fileprivate var _isRFC6455 = false;
    fileprivate var _isStarted = false;
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //#pragma mark Setup and Teardown
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //@synthesize websocketQueue;
    //
    //- (id)initWithRequest:(HTTPMessage *)aRequest socket:(GCDAsyncSocket *)socket
    //{
    //	HTTPLogTrace();
    //	
    //	if (aRequest == nil)
    //	{
    //		return nil;
    //	}
    //	
    //	if ((self = [super init]))
    public init(request aRequest:HTTPMessage,socket:GCDAsyncSocket){
    //	{
    //		if (HTTP_LOG_VERBOSE)
    //		{
    //			NSData *requestHeaders = [aRequest messageData];
    //			
    //			NSString *temp = [[NSString alloc] initWithData:requestHeaders encoding:NSUTF8StringEncoding];
    //			HTTPLogVerbose(@"%@[%p] Request Headers:\n%@", THIS_FILE, self, temp);
    //		}
        //
        //_websocketQueue = DispatchQueue(label: "WebSocket", qos: []);
        _websocketQueue = DispatchQueue(label: "WebSocket");
    		_request = aRequest;
    //
    		_asyncSocket = socket;
        super.init();
        _asyncSocket.setDelegate(self, delegateQueue:_websocketQueue);
    //		
    //		isOpen = NO;
    //		isVersion76 = [[self class] isVersion76Request:request];
    //		isRFC6455 = [[self class] isRFC6455Request:request];
    //		
    //		term = [[NSData alloc] initWithBytes:"\xFF" length:1];
    //	}
    //	return self;
    }
    //
    //- (void)dealloc
    //{
    //	HTTPLogTrace();
    //	
    //	#if !OS_OBJECT_USE_OBJC
    //	dispatch_release(websocketQueue);
    //	#endif
    //	
    //	[asyncSocket setDelegate:nil delegateQueue:NULL];
    //	[asyncSocket disconnect];
    //}
    //
    //- (id)delegate
    //{
    //	__block id result = nil;
    //	
    //	dispatch_sync(websocketQueue, ^{
    //		result = delegate;
    //	});
    //	
    //	return result;
    //}
    //
    //- (void)setDelegate:(id)newDelegate
    //{
    //	dispatch_async(websocketQueue, ^{
    //		delegate = newDelegate;
    //	});
    //}
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //#pragma mark Start and Stop
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    ///**
    // * Starting point for the WebSocket after it has been fully initialized (including subclasses).
    // * This method is called by the HTTPConnection it is spawned from.
    //**/
    open func start(){
    //{
    //	// This method is not exactly designed to be overriden.
    //	// Subclasses are encouraged to override the didOpen method instead.
    //	
    	_websocketQueue.async(execute: {
    		
            if self._isStarted {
                return;
            }
    		self._isStarted = true;
    		
    		if self._isVersion76 {
//    			[self readRequestBody];
    		}
    		else{
//    			[self sendResponseHeaders];
//    			[self didOpen];
    		}
    	});
    }
    //
    ///**
    // * This method is called by the HTTPServer if it is asked to stop.
    // * The server, in turn, invokes stop on each WebSocket instance.
    //**/
    open func stop(){
    	// This method is not exactly designed to be overriden.
    	// Subclasses are encouraged to override the didClose method instead.
    	
    	_websocketQueue.async(execute: {
    		
    		self._asyncSocket.disconnect();
    	});
    }
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //#pragma mark HTTP Response
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //- (void)readRequestBody
    //{
    //	HTTPLogTrace();
    //	
    //	NSAssert(isVersion76, @"WebSocket version 75 doesn't contain a request body");
    //	
    //	[asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_HTTP_REQUEST_BODY];
    //}
    //
    //- (NSString *)originResponseHeaderValue
    //{
    //	HTTPLogTrace();
    //	
    //	NSString *origin = [request headerField:@"Origin"];
    //	
    //	if (origin == nil)
    //	{
    //		NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
    //		
    //		return [NSString stringWithFormat:@"http://localhost:%@", port];
    //	}
    //	else
    //	{
    //		return origin;
    //	}
    //}
    //
    //- (NSString *)locationResponseHeaderValue
    //{
    //	HTTPLogTrace();
    //	
    //	NSString *location;
    //	
    //	NSString *scheme = [asyncSocket isSecure] ? @"wss" : @"ws";
    //	NSString *host = [request headerField:@"Host"];
    //	
    //	NSString *requestUri = [[request url] relativeString];
    //	
    //	if (host == nil)
    //	{
    //		NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
    //		
    //		location = [NSString stringWithFormat:@"%@://localhost:%@%@", scheme, port, requestUri];
    //	}
    //	else
    //	{
    //		location = [NSString stringWithFormat:@"%@://%@%@", scheme, host, requestUri];
    //	}
    //	
    //	return location;
    //}
    //
    //- (NSString *)secWebSocketKeyResponseHeaderValue {
    //	NSString *key = [request headerField: @"Sec-WebSocket-Key"];
    //	NSString *guid = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    //	return [[key stringByAppendingString: guid] dataUsingEncoding: NSUTF8StringEncoding].sha1Digest.base64Encoded;
    //}
    //
    //- (void)sendResponseHeaders
    //{
    //	HTTPLogTrace();
    //	
    //	// Request (Draft 75):
    //	// 
    //	// GET /demo HTTP/1.1
    //	// Upgrade: WebSocket
    //	// Connection: Upgrade
    //	// Host: example.com
    //	// Origin: http://example.com
    //	// WebSocket-Protocol: sample
    //	// 
    //	// 
    //	// Request (Draft 76):
    //	//
    //	// GET /demo HTTP/1.1
    //	// Upgrade: WebSocket
    //	// Connection: Upgrade
    //	// Host: example.com
    //	// Origin: http://example.com
    //	// Sec-WebSocket-Protocol: sample
    //	// Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
    //	// Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
    //	// 
    //	// ^n:ds[4U
    //
    //	
    //	// Response (Draft 75):
    //	// 
    //	// HTTP/1.1 101 Web Socket Protocol Handshake
    //	// Upgrade: WebSocket
    //	// Connection: Upgrade
    //	// WebSocket-Origin: http://example.com
    //	// WebSocket-Location: ws://example.com/demo
    //	// WebSocket-Protocol: sample
    //	// 
    //	// 
    //	// Response (Draft 76):
    //	//
    //	// HTTP/1.1 101 WebSocket Protocol Handshake
    //	// Upgrade: WebSocket
    //	// Connection: Upgrade
    //	// Sec-WebSocket-Origin: http://example.com
    //	// Sec-WebSocket-Location: ws://example.com/demo
    //	// Sec-WebSocket-Protocol: sample
    //	// 
    //	// 8jKS'y:G*Co,Wxa-
    //
    //	
    //	HTTPMessage *wsResponse = [[HTTPMessage alloc] initResponseWithStatusCode:101
    //	                                                              description:@"Web Socket Protocol Handshake"
    //	                                                                  version:HTTPVersion1_1];
    //	
    //	[wsResponse setHeaderField:@"Upgrade" value:@"WebSocket"];
    //	[wsResponse setHeaderField:@"Connection" value:@"Upgrade"];
    //	
    //	// Note: It appears that WebSocket-Origin and WebSocket-Location
    //	// are required for Google's Chrome implementation to work properly.
    //	// 
    //	// If we don't send either header, Chrome will never report the WebSocket as open.
    //	// If we only send one of the two, Chrome will immediately close the WebSocket.
    //	// 
    //	// In addition to this it appears that Chrome's implementation is very picky of the values of the headers.
    //	// They have to match exactly with what Chrome sent us or it will close the WebSocket.
    //	
    //	NSString *originValue = [self originResponseHeaderValue];
    //	NSString *locationValue = [self locationResponseHeaderValue];
    //	
    //	NSString *originField = isVersion76 ? @"Sec-WebSocket-Origin" : @"WebSocket-Origin";
    //	NSString *locationField = isVersion76 ? @"Sec-WebSocket-Location" : @"WebSocket-Location";
    //	
    //	[wsResponse setHeaderField:originField value:originValue];
    //	[wsResponse setHeaderField:locationField value:locationValue];
    //	
    //	NSString *acceptValue = [self secWebSocketKeyResponseHeaderValue];
    //	if (acceptValue) {
    //		[wsResponse setHeaderField: @"Sec-WebSocket-Accept" value: acceptValue];
    //	}
    //
    //	NSData *responseHeaders = [wsResponse messageData];
    //	
    //	
    //	if (HTTP_LOG_VERBOSE)
    //	{
    //		NSString *temp = [[NSString alloc] initWithData:responseHeaders encoding:NSUTF8StringEncoding];
    //		HTTPLogVerbose(@"%@[%p] Response Headers:\n%@", THIS_FILE, self, temp);
    //	}
    //	
    //	[asyncSocket writeData:responseHeaders withTimeout:TIMEOUT_NONE tag:TAG_HTTP_RESPONSE_HEADERS];
    //}
    //
    //- (NSData *)processKey:(NSString *)key
    //{
    //	HTTPLogTrace();
    //	
    //	unichar c;
    //	NSUInteger i;
    //	NSUInteger length = [key length];
    //	
    //	// Concatenate the digits into a string,
    //	// and count the number of spaces.
    //	
    //	NSMutableString *numStr = [NSMutableString stringWithCapacity:10];
    //	long long numSpaces = 0;
    //	
    //	for (i = 0; i < length; i++)
    //	{
    //		c = [key characterAtIndex:i];
    //		
    //		if (c >= '0' && c <= '9')
    //		{
    //			[numStr appendFormat:@"%C", c];
    //		}
    //		else if (c == ' ')
    //		{
    //			numSpaces++;
    //		}
    //	}
    //	
    //	long long num = strtoll([numStr UTF8String], NULL, 10);
    //	
    //	long long resultHostNum;
    //	
    //	if (numSpaces == 0)
    //		resultHostNum = 0;
    //	else
    //		resultHostNum = num / numSpaces;
    //	
    //	HTTPLogVerbose(@"key(%@) -> %qi / %qi = %qi", key, num, numSpaces, resultHostNum);
    //	
    //	// Convert result to 4 byte big-endian (network byte order)
    //	// and then convert to raw data.
    //	
    //	UInt32 result = OSSwapHostToBigInt32((uint32_t)resultHostNum);
    //	
    //	return [NSData dataWithBytes:&result length:4];
    //}
    //
    //- (void)sendResponseBody:(NSData *)d3
    //{
    //	HTTPLogTrace();
    //	
    //	NSAssert(isVersion76, @"WebSocket version 75 doesn't contain a response body");
    //	NSAssert([d3 length] == 8, @"Invalid requestBody length");
    //	
    //	NSString *key1 = [request headerField:@"Sec-WebSocket-Key1"];
    //	NSString *key2 = [request headerField:@"Sec-WebSocket-Key2"];
    //	
    //	NSData *d1 = [self processKey:key1];
    //	NSData *d2 = [self processKey:key2];
    //	
    //	// Concatenated d1, d2 & d3
    //	
    //	NSMutableData *d0 = [NSMutableData dataWithCapacity:(4+4+8)];
    //	[d0 appendData:d1];
    //	[d0 appendData:d2];
    //	[d0 appendData:d3];
    //	
    //	// Hash the data using MD5
    //	
    //	NSData *responseBody = [d0 md5Digest];
    //	
    //	[asyncSocket writeData:responseBody withTimeout:TIMEOUT_NONE tag:TAG_HTTP_RESPONSE_BODY];
    //	
    //	if (HTTP_LOG_VERBOSE)
    //	{
    //		NSString *s1 = [[NSString alloc] initWithData:d1 encoding:NSASCIIStringEncoding];
    //		NSString *s2 = [[NSString alloc] initWithData:d2 encoding:NSASCIIStringEncoding];
    //		NSString *s3 = [[NSString alloc] initWithData:d3 encoding:NSASCIIStringEncoding];
    //		
    //		NSString *s0 = [[NSString alloc] initWithData:d0 encoding:NSASCIIStringEncoding];
    //		
    //		NSString *sH = [[NSString alloc] initWithData:responseBody encoding:NSASCIIStringEncoding];
    //		
    //		HTTPLogVerbose(@"key1 result : raw(%@) str(%@)", d1, s1);
    //		HTTPLogVerbose(@"key2 result : raw(%@) str(%@)", d2, s2);
    //		HTTPLogVerbose(@"key3 passed : raw(%@) str(%@)", d3, s3);
    //		HTTPLogVerbose(@"key0 concat : raw(%@) str(%@)", d0, s0);
    //		HTTPLogVerbose(@"responseBody: raw(%@) str(%@)", responseBody, sH);
    //		
    //	}
    //}
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //#pragma mark Core Functionality
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //- (void)didOpen
    //{
    //	HTTPLogTrace();
    //	
    //	// Override me to perform any custom actions once the WebSocket has been opened.
    //	// This method is invoked on the websocketQueue.
    //	// 
    //	// Don't forget to invoke [super didOpen] in your method.
    //	
    //	// Start reading for messages
    //	[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:(isRFC6455 ? TAG_PAYLOAD_PREFIX : TAG_PREFIX)];
    //	
    //	// Notify delegate
    //	if ([delegate respondsToSelector:@selector(webSocketDidOpen:)])
    //	{
    //		[delegate webSocketDidOpen:self];
    //	}
    //}
    //
    //- (void)sendMessage:(NSString *)msg
    //{	
    //	NSData *msgData = [msg dataUsingEncoding:NSUTF8StringEncoding];
    //	[self sendData:msgData];
    //}
    //
    //- (void)sendData:(NSData *)msgData
    //{
    //    HTTPLogTrace();
    //    
    //    NSMutableData *data = nil;
    //	
    //	if (isRFC6455)
    //	{
    //		NSUInteger length = msgData.length;
    //		if (length <= 125)
    //		{
    //			data = [NSMutableData dataWithCapacity:(length + 2)];
    //			[data appendBytes: "\x81" length:1];
    //			UInt8 len = (UInt8)length;
    //			[data appendBytes: &len length:1];
    //			[data appendData:msgData];
    //		}
    //		else if (length <= 0xFFFF)
    //		{
    //			data = [NSMutableData dataWithCapacity:(length + 4)];
    //			[data appendBytes: "\x81\x7E" length:2];
    //			UInt16 len = (UInt16)length;
    //			[data appendBytes: (UInt8[]){len >> 8, len & 0xFF} length:2];
    //			[data appendData:msgData];
    //		}
    //		else
    //		{
    //			data = [NSMutableData dataWithCapacity:(length + 10)];
    //			[data appendBytes: "\x81\x7F" length:2];
    //			[data appendBytes: (UInt8[]){0, 0, 0, 0, (UInt8)(length >> 24), (UInt8)(length >> 16), (UInt8)(length >> 8), length & 0xFF} length:8];
    //			[data appendData:msgData];
    //		}
    //	}
    //	else
    //	{
    //		data = [NSMutableData dataWithCapacity:([msgData length] + 2)];
    //        
    //		[data appendBytes:"\x00" length:1];
    //		[data appendData:msgData];
    //		[data appendBytes:"\xFF" length:1];
    //	}
    //	
    //	// Remember: GCDAsyncSocket is thread-safe
    //	
    //	[asyncSocket writeData:data withTimeout:TIMEOUT_NONE tag:0];
    //}
    //
    //- (void)didReceiveMessage:(NSString *)msg
    //{
    //	HTTPLogTrace();
    //	
    //	// Override me to process incoming messages.
    //	// This method is invoked on the websocketQueue.
    //	// 
    //	// For completeness, you should invoke [super didReceiveMessage:msg] in your method.
    //	
    //	// Notify delegate
    //	if ([delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)])
    //	{
    //		[delegate webSocket:self didReceiveMessage:msg];
    //	}
    //}
    //
    //- (void)didClose
    //{
    //	HTTPLogTrace();
    //	
    //	// Override me to perform any cleanup when the socket is closed
    //	// This method is invoked on the websocketQueue.
    //	// 
    //	// Don't forget to invoke [super didClose] at the end of your method.
    //	
    //	// Notify delegate
    //	if ([delegate respondsToSelector:@selector(webSocketDidClose:)])
    //	{
    //		[delegate webSocketDidClose:self];
    //	}
    //	
    //	// Notify HTTPServer
    //	[[NSNotificationCenter defaultCenter] postNotificationName:WebSocketDidDieNotification object:self];
    //}
    //
    //#pragma mark WebSocket Frame
    //
    //- (BOOL)isValidWebSocketFrame:(UInt8)frame
    //{
    //	NSUInteger rsv =  frame & 0x70;
    //	NSUInteger opcode = frame & 0x0F;
    //	if (rsv || (3 <= opcode && opcode <= 7) || (0xB <= opcode && opcode <= 0xF))
    //	{
    //		return NO;
    //	}
    //	return YES;
    //}
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //#pragma mark AsyncSocket Delegate
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //// 0                   1                   2                   3
    //// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    //// +-+-+-+-+-------+-+-------------+-------------------------------+
    //// |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
    //// |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
    //// |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    //// | |1|2|3|       |K|             |                               |
    //// +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
    //// |     Extended payload length continued, if payload len == 127  |
    //// + - - - - - - - - - - - - - - - +-------------------------------+
    //// |                               |Masking-key, if MASK set to 1  |
    //// +-------------------------------+-------------------------------+
    //// | Masking-key (continued)       |          Payload Data         |
    //// +-------------------------------- - - - - - - - - - - - - - - - +
    //// :                     Payload Data continued ...                :
    //// + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    //// |                     Payload Data continued ...                |
    //// +---------------------------------------------------------------+
    //
    //- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
    //{
    //	HTTPLogTrace();
    //	
    //	if (tag == TAG_HTTP_REQUEST_BODY)
    //	{
    //		[self sendResponseHeaders];
    //		[self sendResponseBody:data];
    //		[self didOpen];
    //	}
    //	else if (tag == TAG_PREFIX)
    //	{
    //		UInt8 *pFrame = (UInt8 *)[data bytes];
    //		UInt8 frame = *pFrame;
    //		
    //		if (frame <= 0x7F)
    //		{
    //			[asyncSocket readDataToData:term withTimeout:TIMEOUT_NONE tag:TAG_MSG_PLUS_SUFFIX];
    //		}
    //		else
    //		{
    //			// Unsupported frame type
    //			[self didClose];
    //		}
    //	}
    //	else if (tag == TAG_PAYLOAD_PREFIX)
    //	{
    //		UInt8 *pFrame = (UInt8 *)[data bytes];
    //		UInt8 frame = *pFrame;
    //
    //		if ([self isValidWebSocketFrame: frame])
    //		{
    //			nextOpCode = (frame & 0x0F);
    //			[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH];
    //		}
    //		else
    //		{
    //			// Unsupported frame type
    //			[self didClose];
    //		}
    //	}
    //	else if (tag == TAG_PAYLOAD_LENGTH)
    //	{
    //		UInt8 frame = *(UInt8 *)[data bytes];
    //		BOOL masked = WS_PAYLOAD_IS_MASKED(frame);
    //		NSUInteger length = WS_PAYLOAD_LENGTH(frame);
    //		nextFrameMasked = masked;
    //		maskingKey = nil;
    //		if (length <= 125)
    //		{
    //			if (nextFrameMasked)
    //			{
    //				[asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
    //			}
    //			[asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_MSG_WITH_LENGTH];
    //		}
    //		else if (length == 126)
    //		{
    //			[asyncSocket readDataToLength:2 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH16];
    //		}
    //		else
    //		{
    //			[asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH64];
    //		}
    //	}
    //	else if (tag == TAG_PAYLOAD_LENGTH16)
    //	{
    //		UInt8 *pFrame = (UInt8 *)[data bytes];
    //		NSUInteger length = ((NSUInteger)pFrame[0] << 8) | (NSUInteger)pFrame[1];
    //		if (nextFrameMasked) {
    //			[asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
    //		}
    //		[asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_MSG_WITH_LENGTH];
    //	}
    //	else if (tag == TAG_PAYLOAD_LENGTH64)
    //	{
    //		// FIXME: 64bit data size in memory?
    //		[self didClose];
    //	}
    //	else if (tag == TAG_MSG_WITH_LENGTH)
    //	{
    //		NSUInteger msgLength = [data length];
    //		if (nextFrameMasked && maskingKey) {
    //			NSMutableData *masked = data.mutableCopy;
    //			UInt8 *pData = (UInt8 *)masked.mutableBytes;
    //			UInt8 *pMask = (UInt8 *)maskingKey.bytes;
    //			for (NSUInteger i = 0; i < msgLength; i++)
    //			{
    //				pData[i] = pData[i] ^ pMask[i % 4];
    //			}
    //			data = masked;
    //		}
    //		if (nextOpCode == WS_OP_TEXT_FRAME)
    //		{
    //			NSString *msg = [[NSString alloc] initWithBytes:[data bytes] length:msgLength encoding:NSUTF8StringEncoding];
    //			[self didReceiveMessage:msg];
    //		}
    //		else
    //		{
    //			[self didClose];
    //			return;
    //		}
    //
    //		// Read next frame
    //		[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_PREFIX];
    //	}
    //	else if (tag == TAG_MSG_MASKING_KEY)
    //	{
    //		maskingKey = data.copy;
    //	}
    //	else
    //	{
    //		NSUInteger msgLength = [data length] - 1; // Excluding ending 0xFF frame
    //		
    //		NSString *msg = [[NSString alloc] initWithBytes:[data bytes] length:msgLength encoding:NSUTF8StringEncoding];
    //		
    //		[self didReceiveMessage:msg];
    //		
    //		
    //		// Read next message
    //		[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PREFIX];
    //	}
    //}
    //
    //- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error
    //{
    //	HTTPLogTrace2(@"%@[%p]: socketDidDisconnect:withError: %@", THIS_FILE, self, error);
    //	
    //	[self didClose];
    //}
    //
    //@end
}
